package com.cacho.s2b.lesson.grammar;

import com.cacho.s2b.lesson.grammar.func.GrammarFunc;
import com.cacho.s2b.lesson.grammar.func.GrammarFuncGen;
import com.cacho.s2b.lesson.util.JsonUtil;
import com.cacho.s2b.lesson.vo.ApiStepInfo;
import com.cacho.s2b.lesson.vo.ApiTestInfo;
import com.jayway.jsonpath.JsonPath;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang3.StringUtils;

import java.text.ParseException;
import java.util.Objects;

/**
 * @Description 处理成接口需要的格式
 * @Author LingWei
 * @date 2024/06/28
 **/
@Getter
@Setter
public class GrammarParser {
    private ApiTestInfo testInfo;
    private String source = ""; // 处理前的内容
    private String result = ""; // 处理后的内容

    // 空构造函数
    public GrammarParser() {
        this("");
    }

    public GrammarParser(String source) {
        this(source, new ApiTestInfo());
    }

    public GrammarParser(String source, ApiTestInfo testInfo) {
        this.source = source;
        this.testInfo = testInfo;
    }

    // 处理的主函数
    public void parse() {
        String exp = source.substring(2, source.length() - 2); // 去掉前面和后面的{{}}
        if (StringUtils.isEmpty(exp)) {
            this.result = this.source;
            return;
        }
        // date(yyyy-MM-dd'T'HH:mm,Asia/Riyadh,365)
        String rstStep = null;
        try {
            // 只针对#开头的表达式
            rstStep = parseStepExpression(exp);
        } catch (Exception e) {
            // 返回时无该字段：Missing property in path $['Data']['CreditLine']
            System.out.println(e.getMessage());
        }
        // 判断是否相等, 不等为真，请求参数和返回参数时不等
        boolean changedFlag = !Objects.equals(rstStep, exp);

        final String COMMA_FLAG = "::";
        int outLen = -1;
        assert rstStep != null;
        int posOutLen = rstStep.lastIndexOf("),");
        String func = rstStep;
        if (posOutLen > -1) {
            outLen = Integer.parseInt(rstStep.substring(posOutLen + 2).trim());
            func = rstStep.substring(0, posOutLen + 1);
        }
        try {
            this.result = execFunc(func, outLen);
        } catch (NoSuchMethodException | ParseException e) {
            this.result = changedFlag ? rstStep : this.source;
        }
    }

    // 返回String, 处理 feature 文件 断言接口返回参数取值表达式，针对的是#开头的请求和返回参数
    // 返回= #.response.body.Data.AccountId，用这个表达式去拿真实的 值
    private String parseStepExpression(String exp) throws Exception {
        // {{now(yyyy-MM-dd)}} 直接返回，
        if (!exp.startsWith("#")) {
            return exp;
        }
        String temp = exp;
        // 按.分成两部分， 比如 #getToken.response，处理feature文件定义了的stepName
        String[] expParts = exp.split("\\.", 2);
        if (expParts.length < 2) {
            return exp;
        }
        String stepName = expParts[0].substring(1); // 接口请求时stepName有值
        String stepPath = expParts[1];
        try {
            ApiStepInfo step = StringUtils.isEmpty(stepName) ? testInfo.getLastStepInfo() : testInfo.getStep(stepName);
            temp = fetchValueByStep(step, stepPath);
        } catch (Exception e) {
            throw e;
        }
        return temp;
    }

    private String fetchValueByStep(ApiStepInfo step, String stepPath) {
        String temp = String.format("#%s.%s", step, stepPath);  // #Config.response.body.issuer
        String[] parts = stepPath.split("\\.");
        int len = parts.length;
        if (len < 2) {
            return temp;
        }  // 如果小于2 不当成 json 路径
        if ("response".equals(parts[0]) && "body".equals(parts[1])) {
            String body;
            if (step.getEncryptResponse() == null) {
                body = step.getResponse().asString();
            } else {
                body = step.getEncryptResponse();
            }
            if (len == 2) {
                return body;
            } else {
                String jPath = "$" + stepPath.substring("response.body".length());
                return JsonUtil.readAsString(body, jPath);
            }
        } else if ("request".equals(parts[0]) && "body".equals(parts[1])) {
            String body = step.getRequest().getBody();
            if (len == 2) {
                return body;
            } else {
                String jPath = "$" + stepPath.substring("request.body".length());
                return JsonPath.read(body, jPath);
            }
        }
        return temp;
    }

    // 真正实现 GrammarFunc 接口
    private String execFunc(String func, int outLen) throws NoSuchMethodException, ParseException {
        String rtn = "";
        // 判断不为空，并且第一个是不是特殊字符
        if (!StringUtils.isEmpty(func)) {
            char cFirst = func.charAt(0);
            if (!(cFirst >= 65 && cFirst <= 90 || cFirst >= 97 && cFirst <= 122)) { // not a legal function
                throw new NoSuchMethodException(String.format("<%s> is not function expression", func));
            }
        }
        String name = func;
        String params = "";
        // ( 出现在左边的位置
        int leftP = func.indexOf("(");
        // ) 出现在右边的位置
        int rightP = func.lastIndexOf(")");
        // 如果是正常的 ()， 需要处理
        if (rightP > leftP && leftP > -1) {
            params = func.substring(leftP + 1, func.lastIndexOf(")")); // yyyy-MM-dd
            // 获取方法
            name = func.substring(0, func.indexOf("(")).toLowerCase(); // now
        }
        // String.replaceAll()
        params = params.replaceAll("\\\\,", "::");
        String[] parts = params.split(",");
        /* Recover the replaced \c, E.,  \n \r \, */
        for (int i = 0; i < parts.length; i++) {
            parts[i] = parts[i].replace("::", ",");
        }
        // 调 公共方法
        GrammarFunc gf = GrammarFuncGen.getInstance(name);
        if (gf == null) {
            throw new NoSuchMethodException(String.format("<%s> func is not supported", name));
        }
        rtn = gf.getString(testInfo, parts, outLen); // 实现接口
        return rtn;
    }

    public static void main(String[] args) {
        // 表达式从 feature文件来
        String[] varList = {"TEST", "{{jwt(GroupId,100,PS256)}}",
                "{{unique(),20}}", "{{unique}}",
                "{{date}}", "{{date(yyyy-MM-dd'T'HH:mm:ss,Asia/Riyadh,365)}}", "{{date(yyyy-MM-dd'T'HH:mm:ss,GMT+8,-100)}}",
                "{{format(UPPER,ab)}}","{{format(SUBSTRING,Abc123,1,3)}}"
        };
        for (String val : varList) {
            GrammarParser parser = new GrammarParser(val);
            parser.parse();
            System.out.println(parser.getResult());
        }
    }

}
